using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using IQIGame.Rendering;
using System;
using System.Collections.Generic;
using UnityEngine;

namespace IQIGame.Onigao.GamePlay
{
    public class LevelPlaySceneCtrl : TSceneController<LevelSceneLoader>, ISceneBlockAdapter
    {
        public LevelPlayRegionController regionCtrl => parent as LevelPlayRegionController;

        /// <summary>
        /// lightmap加载器
        /// </summary>
        private ResLoader _lightMapLoader;

        /// <summary>
        /// 获取场景最终天气数据的临时集合
        /// </summary>
        private Dictionary<int, int> _tempWeatherType = new Dictionary<int, int>();
        /// <summary>
        /// 天气类型定义
        /// key：枚举值，value：枚举名称
        /// </summary>
        private static Dictionary<int, string> _weatherTypeDefine = new Dictionary<int, string>();

        /// <summary>
        /// 所有天气组件
        /// key为业务id，由编辑器生成，策划可在服务里指定
        /// </summary>
        private Dictionary<int, BehaviorFeatureComp> _weatherComs = new Dictionary<int, BehaviorFeatureComp>();

        static LevelPlaySceneCtrl()
        {
            //需要用到枚举的name，先缓存避免每次ToString都产生新的string对象（已做Benchmark，Enum.ToString()没有缓存，也没有预编译，每次调用都会产生新对象）
            foreach (GameRender.WeatherType value in Enum.GetValues(typeof(GameRender.WeatherType)))
            {
                _weatherTypeDefine.Add((int)value, value.ToString());
            }
        }

        /// <summary>
        /// 首次进入场景的数据
        /// </summary>
        public void SetInitData()
        {

        }

        /// <summary>
        /// 修改时间显示
        /// </summary>
        /// <param name="customTimeHour"></param>
        /// <returns></returns>
        public void ChangeSceneRenderTime(int timeHour)
        {
            if (skybox == null)
            {
                return;
            }
            skybox.SetTimeHour24(timeHour);
        }

        #region 昼夜换Lightmap相关
        /// <summary>
        /// 修改场景Lightmap
        /// </summary>
        /// <param name="lightmapConfig"></param>
        //private void ChangeSceneLightmap(SceneLightmapConfig lightmapConfig)
        //{
        //    if (lightmapConfig == null)
        //    {
        //        return;
        //    }
        //    var lightMapData = LightmapSettings.lightmaps;
        //    for (int i = 0; i < lightMapData.Length; i++)
        //    {
        //        if (i >= lightmapConfig.lightmapDatas.Length)
        //        {
        //            break;
        //        }
        //        var lightDatas = lightmapConfig.lightmapDatas[i];
        //        lightMapData[i].lightmapColor = lightDatas.lightmapColor;
        //        lightMapData[i].lightmapDir = lightDatas.lightmapDir;
        //        lightMapData[i].shadowMask = lightDatas.shadowMask;
        //    }
        //    LightmapSettings.lightmaps = lightMapData;

        //    // 修改GameObject开关
        //    if (lightmapConfig.sceneObjectModifies != null)
        //    {
        //        foreach (var modify in lightmapConfig.sceneObjectModifies)
        //        {
        //            GameObject go = null;
        //            // 查找第一个斜杠的位置
        //            var rootPath = GameCommonUtil.GetGameObjectRootPath(modify.gameObjectPath);
        //            if (rootPath.Length < modify.gameObjectPath.Length)
        //            {
        //                go = GameObject.Find(rootPath);
        //                if (go == null)
        //                {
        //                    continue;
        //                }
        //                // 长度+1，是因为有个"/"
        //                string lastPath = modify.gameObjectPath.Substring(rootPath.Length + 1);
        //                var trans = go.transform.Find(lastPath);
        //                if (trans != null)
        //                {
        //                    trans.gameObject.SetActive(modify.isActive);
        //                }
        //            }
        //            else
        //            {
        //                go = GameObject.Find(modify.gameObjectPath);
        //                if (go != null)
        //                {
        //                    go.SetActive(modify.isActive);
        //                }
        //            }
        //        }
        //    }
        //    // 修改天空盒
        //    if (lightmapConfig.skyBoxMateial != null)
        //    {
        //        RenderSettings.skybox = lightmapConfig.skyBoxMateial;
        //    }
        //}

        #endregion

        #region 地图分块加载

        public SceneBlockController blockController { get; private set; }

        /// <summary>
        /// 最后一个加载，因为不知道角色位置，所以需要服务器告诉位置后才能加载
        /// </summary>
        /// <returns></returns>
        private async UniTask PreloadSceneFinal()
        {
            //开始分块加载
            var sceneCfg = regionCtrl.cfgRegionPack.cfgScene;
            if (!sceneCfg.BlockLoad)
            {
                return;
            }

            SceneBlockConfig sceneBlockConfig = new SceneBlockConfig()
            {
                sceneName = sceneCfg.SceneName,
                sizeX = sceneCfg.BlockCountX,
                sizeY = sceneCfg.BlockCountZ,
                sceneRoot = new GameObject("SceneMap").transform,
                offsetPosition = sceneCfg.BlockOffset.ToVector2(),
            };
            blockController = new SceneBlockController(this, sceneBlockConfig, OnBlockChanged);

            await blockController.LoadSceneBlockOnceAsync(GetCenterPosition());
        }

        public Vector3 GetCenterPosition()
        {
            var mainCharacter = this.regionCtrl.entityCtrl.GetMainCharacter();
            if (mainCharacter != null)
            {
                return mainCharacter.transform.position;
            }
            return Vector3.zero;
        }

        #endregion

        #region 生命周期
        public async UniTask FirstEnterScene()
        {
            _loaderProcess.AddAsyncPreload(PreloadSceneFinal);

            await LoadScene(regionCtrl.cfgRegionPack.cfgScene.SceneName);

            //根据配置或保存数据修改天气变化
            //特殊添加全局天气组件
            GameObject globalWeatherGO = Weather.Instance.GetGlobalWeatherGo();
            if (globalWeatherGO != null)
            {
                _weatherComs.Add(0, globalWeatherGO.GetComponent<BehaviorFeatureComp>());
            }
            //获取当前天气
            GetCurWeathers(_tempWeatherType);
            //根据数据修改所有场景中的天气
            foreach (var kv in _tempWeatherType)
            {
                ChangeWeather(kv.Key, kv.Value, false);
            }
            _tempWeatherType.Clear();
        }

        protected override void OnModuleGameplayInit()
        {
            // 实体默认色阶值相关
            EntityDriverEnvironment.Instance.defaultMatColorValueAction = DefaultEntityMaterialRamp;
            EntityDriverEnvironment.Instance.defaultGroundId = regionCtrl.cfgRegionPack.cfgScene.DefaultGround;
#if UNITY_EDITOR
            EditorBehaviourHelper.AddApplicationQuitEvent(ClearEntityDriverEnvironment);
#endif
        }

        protected override void OnSceneLoaded()
        {
            MsgDispatcher.AddListener<int, int>(MsgEventType.Level_NotifyChangeWeather, OnNotifyChangeWeather);

            HUDNumberRender.Instance.Initialize(PathConstant.GetHUDConfigPath(), regionCtrl.cameraCtrl.cinemachineBrain.GetComponent<Camera>());

            ChangeSceneRenderTime(regionCtrl.levelPlayCtrl.curTimeHour);
        }

        public void DefaultEntityMaterialRamp(IMaterialChangeable materialChangeable)
        {
            //materialChangeable.MatSetFloat("_RampLevel", regionCtrl.cfgRegionPack.cfgScene.DefaultRamp);
        }

        public void ClearEntityDriverEnvironment()
        {
            EntityDriverEnvironment.Instance.Clear();
#if UNITY_EDITOR
            EditorBehaviourHelper.RemoveApplicationQuitEvent(ClearEntityDriverEnvironment);
#endif
        }

        protected override void OnUpdate(float deltaTime)
        {
            base.OnUpdate(deltaTime);

            if (blockController != null)
            {
                blockController.Update(deltaTime);
            }
        }

        protected override void OnDispose()
        {
            MsgDispatcher.RemoveListener<int, int>(MsgEventType.Level_NotifyChangeWeather, OnNotifyChangeWeather);
            HUDNumberRender.Instance.Dispose();
            ClearEntityDriverEnvironment();

            if (_lightMapLoader != null)
            {
                _lightMapLoader.Put2Pool();
                _lightMapLoader = null;
            }

            if (blockController != null)
            {
                blockController.Dispose();
                blockController = null;
            }

            _weatherComs.Clear();
        }
        #endregion

        private void OnBlockChanged(bool isToRemoved, GameObject blockGameObject)
        {
            regionCtrl.displayObjCtrl.OnSceneBlockChanged(isToRemoved, blockGameObject);
            UpdateWeatherObj(isToRemoved, blockGameObject);
        }

        private void UpdateWeatherObj(bool isToRemoved, GameObject blockGameObject)
        {
            var objPathIdMap = regionCtrl.levelRegion.packConfig.gamePlayConfig.weatherObjPathIDs;
            if (objPathIdMap == null || objPathIdMap.Count == 0)
                return;
            LocalWeather[] allWeatherComs = blockGameObject.GetComponentsInChildren<LocalWeather>(true);
            // 检查当前parent下的所有子对象
            foreach (LocalWeather weatherObj in allWeatherComs)
            {
                //如果该对象可在配置中的天气对象列表里获取到，则当做局部天气对象处理
                if (objPathIdMap.TryGetValue(weatherObj.transform.GetFullPath(), out int weatherid))
                {
                    if (isToRemoved)
                    {
                        _weatherComs.Remove(weatherid);
                    }
                    else
                    {
                        BehaviorFeatureComp comp = weatherObj.GetComponent<BehaviorFeatureComp>();
                        if (comp == null)
                        {
                            LogGame.LogError($"更新天气组件出错，对象上没有BehaviorFeatureComp组件。weatherid：{weatherid}");
                        }
                        else if (!_weatherComs.TryAdd(weatherid, comp))
                        {
                            LogGame.LogError($"更新天气组件出错，天气id已存在。weatherid：{weatherid}");
                        }
                        else
                        {
                            //如果自定义天气有值，分块加载出来以后要刷一次，因为这个地图块有可能是被销毁后重新Instantiate的，不刷新不是实时天气的表现
                            if (regionCtrl.levelRegion.customWeathers.TryGetValue(weatherid, out var weatherType))
                            {
                                //直接取值，不要用TryGetValue，这里必须是有值的
                                var typeName = _weatherTypeDefine[weatherType];
                                comp.PlayGroup(typeName);
                            }
                        }
                    }
                }
                else
                {
                    LogGame.LogError($"更新天气组件出错，根据LocalWeather的路径{weatherObj.transform.GetFullPath()}在配置里找不到数据");
                }
            }
        }

        private void ChangeWeather(int id, int type, bool needAnimation)
        {
            if (id == 0 || !needAnimation) //实时设置全局天气
            {
                Weather.Instance.SetWeather(type);
            }
            else //动画天气（包括全局天气和局部天气）
            {
                if (_weatherComs.TryGetValue(id, out var comp))
                {
                    //直接取值，不要用TryGetValue，这里必须是有值的
                    var typeName = _weatherTypeDefine[type];
                    comp.PlayGroup(typeName);
                }
            }
        }

        private void GetCurWeathers(Dictionary<int, int> tempWeatherType)
        {
            //默认天气
            foreach (var kv in regionCtrl.levelRegion.packConfig.gamePlayConfig.weatherTypes)
            {
                tempWeatherType[kv.Key] = kv.Value;
            }
            //自定义天气（覆盖默认天气）
            foreach (var kv in regionCtrl.levelRegion.customWeathers)
            {
                tempWeatherType[kv.Key] = kv.Value;
            }
        }

        private void OnNotifyChangeWeather(int id, int type)
        {
            if (regionCtrl.levelRegion.ChangeWeather(id, type))
            {
                ChangeWeather(id, type, true);
            }
        }
    }
}
